package cn.csg.iotplatform.module.system.service.org;

import cn.csg.iotplatform.framework.datasource.core.enums.DataSourceEnum;
import cn.csg.iotplatform.module.system.dal.dataobject.dept.DeptDO;
import cn.csg.iotplatform.module.system.dal.dataobject.dept.UserDeptRelationDto;
import cn.csg.iotplatform.module.system.dal.dataobject.org.EquipCenterOrgDO;
import cn.csg.iotplatform.module.system.dal.dataobject.org.RemoteOrgDO;
import cn.csg.iotplatform.module.system.dal.dataobject.org.RemoteUserOrgDO;
import cn.csg.iotplatform.module.system.dal.dataobject.org.vo.RemoteUserOrgRelVO;
import cn.csg.iotplatform.module.system.dal.dataobject.user.AdminUserDO;
import cn.csg.iotplatform.module.system.dal.mysql.dept.DeptMapper;
import cn.csg.iotplatform.module.system.dal.mysql.org.EquipCenterOrgMapper;
import cn.csg.iotplatform.module.system.dal.mysql.org.RemoteOrgMapper;
import cn.csg.iotplatform.module.system.dal.mysql.org.RemoteUserOrgMapper;
import cn.csg.iotplatform.module.system.dal.mysql.user.AdminUserMapper;
import cn.csg.iotplatform.module.system.service.dept.DeptService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.google.common.collect.Maps;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrgService {

    @Resource
    private OrgService orgService;
    @Resource
    private RemoteOrgMapper remoteOrgMapper;
    @Resource
    private RemoteUserOrgMapper remoteUserOrgMapper;
    @Resource
    private DeptMapper deptMapper;
    @Resource
    private AdminUserMapper adminUserMapper;
    @Resource
    private DeptService deptService;
    @Resource
    private EquipCenterOrgMapper equipCenterOrgMapper;

    private static long maxDeptId;

    @PostConstruct
    public void initMaxDeptId() {
        maxDeptId = deptMapper.selectMaxId();
        maxDeptId = Math.max(maxDeptId,100000L);
    }

    @XxlJob("synRemoteOrgData")
    public void synRemoteOrgData2Local(String param) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        if(Boolean.parseBoolean(param)){
            deptMapper.deleteAll();
            adminUserMapper.deleteAll();
        }
        List<RemoteOrgDO> remoteDepts = Lists.newArrayList();
        List<RemoteUserOrgDO> remoteUserDepts = Lists.newArrayList();
        List<RemoteUserOrgRelVO> remoteUserOrgRels = Lists.newArrayList();
        getRemteOrgData(remoteDepts,remoteUserDepts,remoteUserOrgRels);
        orgService.updateRemoteOrgData2Local(remoteDepts,remoteUserDepts,remoteUserOrgRels);
        deptService.updateFullPath();
        stopWatch.stop();
        System.out.println(String.format("组织机构数据同步结束，耗时=%ss",stopWatch.getTotalTimeSeconds()));
    }

    @XxlJob("synRemoteEquipCenterOrgData2Local")
    public void synRemoteEquipCenterOrgData2Local() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        List<EquipCenterOrgDO> equipCenterOrgs = getEquipCenterOrgData();
        updateEquipCenterOrgData2Local(equipCenterOrgs);
        deptService.updateFullPath();
        stopWatch.stop();
        System.out.println(String.format("设备中心组织机构数据同步结束，耗时=%ss",stopWatch.getTotalTimeSeconds()));
    }

    private List<EquipCenterOrgDO> getEquipCenterOrgData() {
        try{
            DynamicDataSourceContextHolder.push("equipmentCenter");
            return equipCenterOrgMapper.selectList();
        }finally {
            DynamicDataSourceContextHolder.clear();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateEquipCenterOrgData2Local(List<EquipCenterOrgDO> equipCenterOrgs) {
        Map<String,EquipCenterOrgDO> equipCenterOrgMap = equipCenterOrgs.stream().collect(Collectors.toMap(EquipCenterOrgDO::getFullPathName, e -> e,(k1, k2)->k1));
        List<DeptDO> localDepts = deptMapper.selectList();
        Map<String,DeptDO> localDeptMap = localDepts.stream().collect(Collectors.toMap(DeptDO::getFullPathName, e -> e,(k1, k2)->k1));
        List<DeptDO> updateDepts = new ArrayList<>();
        List<DeptDO> addDepts = new ArrayList<>();
        Map<String,Long> orgId2DeptIdMap = Maps.newHashMap();
        Map<String,String> orgId2ParentIdMap = equipCenterOrgMap.values().stream().collect(Collectors.toMap(EquipCenterOrgDO::getOrgId,EquipCenterOrgDO::getParentOrgId,(k1, k2)->k1));
        Set<Long> shareDeptIds = new HashSet<>();
        for(Map.Entry<String,EquipCenterOrgDO> equip : equipCenterOrgMap.entrySet()){
            CopyOptions copyOptions = new CopyOptions();
            final List<String> fileds = Arrays.asList("fullPathName","name","orgId","orgCode","orgType","orgLevel","orgStructureId","orgLevel","shortSpell","fullSpell","areaId","sapHrOrgId");
            copyOptions.setPropertiesFilter((field,object)-> {
                return fileds.contains(field.getName());
            });
            //如果物联存在就更新
            if(localDeptMap.containsKey(equip.getKey())){
                DeptDO deptDO = localDeptMap.get(equip.getKey());
                deptDO.setIs4A(1);
                EquipCenterOrgDO equipCenterOrgDO = equip.getValue();
                BeanUtil.copyProperties(equipCenterOrgDO,deptDO,copyOptions);
                deptDO.setEmail(equipCenterOrgDO.getEmailAddress());
                updateDepts.add(deptDO);
                shareDeptIds.add(deptDO.getId());
                orgId2DeptIdMap.put(equipCenterOrgDO.getOrgId(),deptDO.getId());
            }else{
                //如果物联不存在就新增
                DeptDO deptDO = new DeptDO();
                deptDO.setId(++maxDeptId);
                deptDO.setIs4A(1);
                EquipCenterOrgDO equipCenterOrgDO = equip.getValue();
                BeanUtil.copyProperties(equipCenterOrgDO,deptDO,copyOptions);
                deptDO.setEmail(equipCenterOrgDO.getEmailAddress());
                deptDO.setSort(equipCenterOrgDO.getSortNo());
                fillUnitCode(deptDO);
                addDepts.add(deptDO);
                orgId2DeptIdMap.put(equipCenterOrgDO.getOrgId(),deptDO.getId());
            }
        }
        addDepts.forEach(e->{
            if(orgId2ParentIdMap.containsKey(e.getOrgId())){
                String parentOrgId = orgId2ParentIdMap.get(e.getOrgId());
                if(orgId2DeptIdMap.containsKey(parentOrgId)){
                    e.setParentId(orgId2DeptIdMap.get(parentOrgId));
                }else if(parentOrgId.equals("-1")){
                    e.setParentId(0L);
                }else{
                    e.setParentId(0L);
                    log.error("设备中心组织机构数据同步失败，父级组织机构不存在，orgId={},parentId={}",e.getOrgId(),parentOrgId);
                }
            }else{
                e.setParentId(0L);
                log.error("设备中心组织机构数据同步失败，父级组织机构不存在，orgId={}",e.getOrgId());
            }
        });
        localDepts = localDepts.stream().filter(e->!shareDeptIds.contains(e.getId())).collect(Collectors.toList());
        localDepts.forEach(e->e.setIs4A(0));
        if(localDepts.size()>0)deptMapper.updateBatch(localDepts,1000);
        if(addDepts.size()>0)deptMapper.insertBatch(addDepts,1000);
        if(updateDepts.size()>0)deptMapper.updateBatch(updateDepts,1000);
        log.info("新增记录数:{}，更新记录数{}，删除记录数{}",addDepts.size(),updateDepts.size(),localDepts.size());
    }

    private void fillUnitCode(DeptDO deptDO) {
        if(NumberUtil.isNumber(deptDO.getOrgCode())){
            if(deptDO.getOrgCode().length()>=2){
                deptDO.setProvinceUnit(deptDO.getOrgCode().substring(0,2));
            }
            if(deptDO.getOrgCode().length()>=4){
                deptDO.setCityUnit(deptDO.getOrgCode().substring(0,4));
            }
            if(deptDO.getOrgCode().length()>=6){
                deptDO.setDistrictUnit(deptDO.getOrgCode().substring(0,6));
            }
        }
    }

    public void getRemteOrgData(List<RemoteOrgDO> remoteDepts, List<RemoteUserOrgDO> remoteUserDepts, List<RemoteUserOrgRelVO> remoteUserOrgRelDOS){
        DynamicDataSourceContextHolder.push(DataSourceEnum.PLATFORMUSING);
        remoteDepts.addAll(remoteOrgMapper.selectOrgList());
        remoteUserDepts.addAll(remoteUserOrgMapper.selectUserOrgList());
        remoteUserOrgRelDOS.addAll(remoteUserOrgMapper.selectUserOrgRelList());
        DynamicDataSourceContextHolder.clear();
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateRemoteOrgData2Local(List<RemoteOrgDO> remoteDepts, List<RemoteUserOrgDO> remoteUserDepts,List<RemoteUserOrgRelVO> remoteUserOrgRels){
        Set<String> remoteDeptCodeSet = remoteDepts.stream().map(RemoteOrgDO::getOrgCode).collect(Collectors.toSet());
        List<DeptDO> localDepts = deptMapper.selectList();
        localDepts = localDepts.stream().filter(e-> StrUtil.isNotBlank(e.getOrgCode())).collect(Collectors.toList());
        Map<String,DeptDO> localDeptMap = localDepts.stream().collect(Collectors.toMap(DeptDO::getOrgCode, e -> e,(k1, k2)->k1));
        Set<String> localDeptCodes = localDeptMap.keySet();
        Set<String> addDepts = new HashSet<>(remoteDeptCodeSet);
        addDepts.removeAll(localDeptCodes);
        Set<RemoteOrgDO> addOrgDepts = remoteDepts.stream().filter(e->addDepts.contains(e.getOrgCode())).collect(Collectors.toSet());
        List<DeptDO> addSysDepts = Lists.newArrayList();
        for(RemoteOrgDO orgDO : addOrgDepts){
            if(orgDO.getOrgId().equals("10000")) continue;
            DeptDO deptDO = new DeptDO();
            BeanUtil.copyProperties(orgDO,deptDO,"parentId");
            deptDO.setId(++maxDeptId);
            addSysDepts.add(deptDO);
        }
        if(addSysDepts.size() > 0) deptMapper.insertBatch(addSysDepts,1000);
        localDepts = deptMapper.selectList();
        localDepts = localDepts.stream().filter(e->StrUtil.isNotBlank(e.getOrgCode())).collect(Collectors.toList());
        Map<String,Long> localDeptCode2DeptIdMap = localDepts.stream().collect(Collectors.toMap(DeptDO::getOrgCode,DeptDO::getId,(k1,k2)->k1));
        Map<String, RemoteOrgDO> remoteDeptCodeMap = remoteDepts.stream().collect(Collectors.toMap(RemoteOrgDO::getOrgCode, e->e,(k1, k2)->k1));
        Set<String> delDepts = new HashSet<>(localDeptCodes);
        delDepts.removeAll(remoteDeptCodeSet);
        for(DeptDO dept : localDepts){
            if(dept.getOrgCode().equals("10000")) continue;
            if(delDepts.contains(dept.getOrgCode())) dept.setDeleted(true);
            if(remoteDeptCodeMap.containsKey(dept.getOrgCode())){
                RemoteOrgDO orgDO = remoteDeptCodeMap.get(dept.getOrgCode());
                if(StrUtil.isNotBlank(orgDO.getParentCode())){
                    dept.setParentId(localDeptCode2DeptIdMap.getOrDefault(orgDO.getParentCode(),100L));
                }
                BeanUtil.copyProperties(orgDO,dept,"parentId");
            }
        }
        if(localDepts.size() > 0) deptMapper.updateBatch(localDepts,1000);
        Map<String, RemoteUserOrgDO> remoteUserDeptMap = remoteUserDepts.stream().filter(e->!"admin".equalsIgnoreCase(e.getUsername())).collect(Collectors.toMap(RemoteUserOrgDO::getUsername, e -> e,(k1, k2)->k1));
        List<AdminUserDO> localUsers = adminUserMapper.selectList().stream().filter(e->!"admin".equalsIgnoreCase(e.getUsername())).collect(Collectors.toList());
        Map<String,AdminUserDO> existUserMap = localUsers.stream().collect(Collectors.toMap(AdminUserDO::getUsername,e -> e,(k1,k2)->k1));
        Set<String> existUserAccs = existUserMap.keySet();
        Set<String> addUserAccs = new HashSet<>(remoteUserDeptMap.keySet());
        addUserAccs.removeAll(existUserAccs);
        Set<RemoteUserOrgDO> addUsers = remoteUserDepts.stream().filter(e->addUserAccs.contains(e.getUsername())).collect(Collectors.toSet());

        Map<String, RemoteOrgDO> remoteDeptIdMap = remoteDepts.stream().collect(Collectors.toMap(RemoteOrgDO::getOrgId, e->e,(k1, k2)->k1));
        localDeptMap = localDepts.stream().collect(Collectors.toMap(DeptDO::getOrgCode, e -> e));
        List<AdminUserDO> addSysUsers = Lists.newArrayList();
        Set<String> userNameSets = new HashSet<>();
        for(RemoteUserOrgDO userOrgDO : addUsers){
            if(userNameSets.contains(userOrgDO.getUsername())) continue;
            userNameSets.add(userOrgDO.getUsername());
            AdminUserDO adminUserDO = new AdminUserDO();
            BeanUtil.copyProperties(userOrgDO,adminUserDO,"id","deptId");
            setUserDeptData(adminUserDO,remoteDeptIdMap,localDeptMap,userOrgDO.getDeptId());
            addSysUsers.add(adminUserDO);
        }
        if(addSysUsers.size() > 0)adminUserMapper.insertBatch(addSysUsers, 1000);
        Set<String> delUserAccs = new HashSet<>(existUserAccs);
        delUserAccs.removeAll(remoteUserDeptMap.keySet());
        localUsers = adminUserMapper.selectList();
        for(AdminUserDO adminUserDO : localUsers){
            if(delUserAccs.contains(adminUserDO.getUsername())) {
                adminUserDO.setDeleted(true);
            }else{
                RemoteUserOrgDO userOrgDO = remoteUserDeptMap.get(adminUserDO.getUsername());
                if(userOrgDO == null) {
                    log.error("查询不到远端用户数据,acc={}",adminUserDO.getUsername());
                }else{
                    setUserDeptData(adminUserDO,remoteDeptIdMap,localDeptMap,userOrgDO.getDeptId());
                    BeanUtil.copyProperties(userOrgDO,adminUserDO,"id","deptId");
                }
            }
        }
        if(localUsers.size() > 0)adminUserMapper.updateBatch(localUsers,1000);
        Map<String,DeptDO> orgId2DeptMap = localDepts.stream().collect(Collectors.toMap(DeptDO::getOrgId, e -> e,(k1, k2)->k1));
        Map<String,AdminUserDO> userName2UserMap = localUsers.stream().collect(Collectors.toMap(AdminUserDO::getUsername,e->e,(k1,k2)->k1));
        List<UserDeptRelationDto> userDeptRelations = Lists.newArrayList();
        for(RemoteUserOrgRelVO vo : remoteUserOrgRels){
            if(orgId2DeptMap.containsKey(vo.getDeptId()) && userName2UserMap.containsKey(vo.getUserAccount())){
                UserDeptRelationDto userDeptRelationDto = new UserDeptRelationDto();
                userDeptRelationDto.setUserId(userName2UserMap.get(vo.getUserAccount()).getId());
                userDeptRelationDto.setDeptId(orgId2DeptMap.get(vo.getDeptId()).getId());
                userDeptRelations.add(userDeptRelationDto);
            }
        }
        userDeptRelations.stream().filter(e->deptMapper.selectUserDeptRelation(e.getUserId(),e.getDeptId()) == null);
        if(userDeptRelations.size() > 0)deptMapper.insertUserDeptRelationBatch(userDeptRelations);
    }

    private void setUserDeptData(AdminUserDO sysUser,Map<String,RemoteOrgDO> remoteDeptIdMap,Map<String,DeptDO> localDeptMap,String remoteDeptId){
        RemoteOrgDO remoteOrgDO = remoteDeptIdMap.get(remoteDeptId);
        if(remoteOrgDO == null) {
            log.error("查询不到远端机构数据,deptId={}",remoteDeptId);
        }else{
            DeptDO sysDept = localDeptMap.get(remoteOrgDO.getOrgCode());
            if(sysDept == null){
                log.error("查询不到本地机构数据,deptCode={}",remoteOrgDO.getOrgCode());
            }else{
                sysUser.setDeptId(sysDept.getId());
            }
        }
    }


}
